/* hwInterfaceServer.c */

/***********************************************************************
*                    Copyright Aquantia Corporation
*                                Freeware
*
***********************************************************************/

/*! \file
    This file contains the relay functions for the hardware interface for the TCP client.
*/

#include "hwInterfaceBase.h"
#include "tcpServer.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef __cplusplus
  extern "C" {
#endif

/*! \defgroup ServerDebug Server Debug
This module contains flags for server debug.  These are instantiated in server.c 
@{*/

/*! When set, this dumps additional debug information to the FILE handle TCP_VerboseServerOutput */
extern unsigned int TCP_VERBOSE;

/*! The FILE handle where server debugging information is written to.  This initializes to stdout. */
extern FILE* TCP_VerboseServerOutput;

/*! The FILE handle where server error information is written to.  This initializes to stderr. */
extern FILE* TCP_ErrorServerOutput;

/*@}*/


/*! \addtogroup TCPServer
 @{*/

/*****************************************************************************

                                 ExternalMDIO

*****************************************************************************/


#ifdef LOCKING_AND_TRACKING_SUPPORT
/*! \defgroup StateTracking State Tracking
 @{*/
/*! A structure used to track locks on a per PHY basis.  If a PHY is locked, only the opening socket can talk to it */
typedef struct PHY_State
{
  unsigned int locked;              /*!< A flag indicating whether the PHY is locked - i.e. only a process on a specific socket can use it */
  SOCKET lockingSocketID;           /*!< If locked, the ID of the socket that locked it.                                                   */
} PHY_State;

/*! A structure used to track the initialization of a device, used for for Adapters and hwInterface. */
typedef struct Opened_State
{
  SOCKET openingSocketID[MAX_NUMBER_OF_CLIENTS];          /*!< An array of socket IDs that opened this device.  The last one to close it will actually close it. */
  unsigned int numberOfActiveSockets;                     /*!< The number of active sockets sharing a device */
} Opened_State;

/*! A structure used to track sockets and opened status on a per Adapter basis. */
typedef struct Adapter_State
{
  unsigned int locked;                                    /*!< A flag indicating whether the adapter is locked - i.e. only a process on a specific socket can call the board functions associated with this adapter */
  SOCKET lockingSocketID;                                 /*!< If locked, the ID of the socket that locked it.                                                                                                      */
  Opened_State state;                                     /*!< The opened state of the adapter                                                                                                                      */
  PHY_State phyArray[MAX_NUMBER_OF_PHYS];                 /*!< An array of PHYs associated with this Adapter                                                                                                        */
} Adapter_State;

/*! An array of adapter states used to track locks and opened states, so that if a socket disconnects, all open adapters with it can be closed.  This is populated
    upon initialization of an adapter. */
Adapter_State* AdapterArray;

/*! The opened state of the hardware interface */
Opened_State HW_InterfaceInitializedState;




/*! This function performs initialization on an Opened_State. */
void InitializeOpenedState (Opened_State* state)
{
  /* declare local variables */
  unsigned int i;
  
  for (i = 0; i < MAX_NUMBER_OF_CLIENTS; i++)
    state->openingSocketID[i] = 0;
    
  state->numberOfActiveSockets = 0;
  return;
}


/*! This function is used to add a socket into an array of sockets.  If there is no room, the function returns zero.  If this is the first socket it returns 1,
    and if this is not the first socket, it returns 2, or if this socket was already in the list it returns 3. */
unsigned int AddSocket (SOCKET socketID, Opened_State* state)
{
  /* declare local variables */
  unsigned int i;
  
  /* check if this is the first socket */
  if (!state->numberOfActiveSockets)
  {
    state->openingSocketID[0] = socketID;
    state->numberOfActiveSockets = 1;
    return 1;
  }
  
  /* check if the socket is in the list and return 3 if it is */
  for (i = 0; i < state->numberOfActiveSockets; i++)
  {
    if (state->openingSocketID[i] == socketID)
    {
      /* socket already is in the list */
      return 3;
    }
  }  /* for (i = 0; i < state->numberOfActiveSockets; i++) */
  
  /* socket is not in the list, and is not the first socket - check if we have room 
     for another socket.  Note that this is superfluous as the server limits the number
     of sockets it will accept */
  if (state->numberOfActiveSockets < MAX_NUMBER_OF_CLIENTS)
  {
    /* add it onto the end of the list */
    state->openingSocketID[state->numberOfActiveSockets++] = socketID;
    return 2;
  }
  
  /* there is no room left */
  return 0;
} 


/*! This function is used to delete a socket from an array of sockets.  If there is no room, the function returns zero, otherwise it returns 1
    if this is the last socket, and 2 if there are still other sockets in the list */
unsigned int DeleteSocket (SOCKET socketID, Opened_State* state)
{
  /* declare local variables */
  unsigned int i, foundSocketID;
  
  if (TCP_VERBOSE)
  {
    fprintf (TCP_VerboseServerOutput, "DeleteSocket(%i) on state with %i opened sockets\n", socketID, state->numberOfActiveSockets);
    for (i = 0; i < state->numberOfActiveSockets; i++)
      fprintf (TCP_VerboseServerOutput, "  %i - %i\n", i, state->openingSocketID[i]);
  }
            
  /* check if the socket is in the list */
  foundSocketID = 0;
  for (i = 0; i < state->numberOfActiveSockets; i++)
  {
    /* if we haven't found the socket ID, check if this is it */
    if (!foundSocketID)
    {
      if (state->openingSocketID[i] != socketID) continue;
      else       
        /* we have a match - mark it for future loops*/
        foundSocketID = 1;
    }
      
    /* note - we can only get here if foundSocketID = 1 */  
    if (i < MAX_NUMBER_OF_CLIENTS - 1)
      /* we aren't on the last position - shift the IDs down one */
      state->openingSocketID[i] = state->openingSocketID[i+1];
    else
      /* the list is full and we have found the socket in the last position - therefore zero it */
      state->openingSocketID[i] = 0;
  }  /* for (i = 0; i < state->numberOfActiveSockets; i++) */

  /* check if we found the socket */  
  if (foundSocketID)
  {
    /* decrement the number of active sockets, and it non-zero return 2 */
    state->numberOfActiveSockets--;
    if (TCP_VERBOSE)
    {
      fprintf (TCP_VerboseServerOutput, "DeleteSocket(%i) completed with %i opened sockets\n", socketID, state->numberOfActiveSockets);
      for (i = 0; i < state->numberOfActiveSockets; i++)
        fprintf (TCP_VerboseServerOutput, "  %i - %i\n", i, state->openingSocketID[i]);
    }
    if (state->numberOfActiveSockets)
      return 2;
    else
      return 1;
  }
  
  if (TCP_VERBOSE)
    fprintf (TCP_VerboseServerOutput, "Socket ID not found in list in DeleteSocket(%i)\n", socketID);
  return 0;
} 

/*! This function is used to find the index of a socket ID in the TCP_Socket_List of active TCP connections */
int FindSocketID_Index (SOCKET socketID, TCP_Socket_List* tcpSocketList)
{
  /* declare local variables */
  unsigned int i;
  
  for (i = 0; i < tcpSocketList->numberOfClients; i++)
    if (socketID == tcpSocketList->socketID[i])
      return i;

  /* socket ID not found */
  return -1; 
}

/*! Internal function to identify whether the PHY is locked by another process */
unsigned int IsPhyLocked
(
  TCP_Socket_List* tcpSocketList, 
  uint8_t* TxBuffer, 
  int* TxBufferSize, 
  unsigned int adapter, 
  unsigned int PHY_ID, 
  SOCKET socketID, 
  const char* operation
)
{
  unsigned int socketID_Index;
  
  if ((AdapterArray[adapter].phyArray[PHY_ID].locked == 0) || 
      (AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID == socketID))
    return 0;

  TxBuffer[0] = 1;
  if ((socketID_Index = FindSocketID_Index (AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID, tcpSocketList) >= 0))
  {
    sprintf ((char*) &TxBuffer[1], "Cannot perform %s as socket is locked by %s at %s", operation,
            tcpSocketList->hostName[socketID_Index], tcpSocketList->ipAddress[socketID_Index]);
  }
  else
  {
    sprintf ((char*) &TxBuffer[1], "Cannot perform %s as socket is locked by unknown socket ID %i.  This should not occur.",
            operation, AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID);
  }
  *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
  
  if (TCP_VERBOSE)
    fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
  return 1;
}

/*! Internal function to identify whether the adapter is locked by another process */
unsigned int IsAdapterLocked
(
  TCP_Socket_List* tcpSocketList, 
  uint8_t* TxBuffer, 
  int* TxBufferSize, 
  unsigned int adapter, 
  SOCKET socketID, 
  const char* operation
)
{
  unsigned int socketID_Index;
  
  if ((AdapterArray[adapter].locked == 0) || 
      (AdapterArray[adapter].lockingSocketID != socketID))
    return 0;

  TxBuffer[0] = 1;
  if ((socketID_Index = FindSocketID_Index (AdapterArray[adapter].lockingSocketID, tcpSocketList) >= 0))
  {
    sprintf ((char*) &TxBuffer[1], "Cannot perform %s as socket is locked by %s at %s", operation,
            tcpSocketList->hostName[socketID_Index], tcpSocketList->ipAddress[socketID_Index]);
  }
  else
  {
    sprintf ((char*) &TxBuffer[1], "Cannot perform %s as socket is locked by unknown socket ID %i.  This should not occur.",
            operation, AdapterArray[adapter].lockingSocketID);
  }
  *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
  
  if (TCP_VERBOSE)
    fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
  return 1;
}

#endif


/*! This global variable indicates whether the server supports block operations on the local interfaces.  This is initialized during the HW_Initialize() call. */
unsigned int LocalBlockOperationSupport;
                                








/*! This function is designed to relay a TCP client message to the local hwInterace.  Abstraction is done on the client side.
    \verbatim  Three compile definitions exist for this functions:
    1) BOARD_SUPPORT: this enables platform-level control of the adapter
    2) BLOCK_OPERATION_SUPPORT: this enables support for block operations
    3) LOCKING_AND_TRACKING_SUPPORT: this enables locking adapters and PHYs, as well as tracking multiple TCP clients simultaneously
                                     accessing local resources.
    
    At a bare minimum, the function requires the following from the local hwInterface:    
      unsigned int HW_Fail ();
      char* HW_Error ();
      unsigned int HW_Initialize ();
      unsigned int HW_NumberOfDevices ();
      char** HW_DeviceList ();
      unsigned int HW_OpenAdapter (unsigned int adapter);
      unsigned int HW_CloseAdapter (unsigned int adapter);
      void HW_Close ();
      void HW_Write (unsigned int adapter, unsigned int PHY_ID, unsigned int MMD, unsigned int address, unsigned int data);
      unsigned int HW_Read (unsigned int adapter, unsigned int PHY_ID, unsigned int MMD, unsigned int address);
      unsigned int HW_GetDefaultAdapter ();
      unsigned int HW_SetDefaultAdapter (unsigned int adapter);
      unsigned int AQ_API_MDIO_SupportsBlockOperations ();
    
    Adding block operation support requires:
      unsigned int AQ_API_MDIO_MaxBlockOperations ();
      unsigned int HW_BlockReadAdapter (unsigned int adapter, unsigned int PHY_ID, unsigned int MMD, unsigned int address);
      void HW_BlockWriteAdapter (unsigned int adapter, unsigned int PHY_ID, unsigned int MMD, unsigned int address, unsigned int data);
      unsigned int* HW_BlockOperationExecuteAdapter (unsigned int adapter, unsigned int PHY_ID);
      unsigned int HW_BlockReadQueueSize ();
    
    Adding board operation support requires:
      void Board_WriteAdapter (unsigned int adapter, unsigned int address, unsigned int data);
      unsigned int Board_ReadAdapter (unsigned int adapter, unsigned int address);
    \endverbatim
*/
unsigned int relayMessage
(
  const int currentSocketID_Index,  /* INPUT -  The index in the TCP_Socket_List of the socket requesting the transaction              */
  TCP_Socket_List* tcpSocketList,   /* INPUT - A pointer to the TCP_Socket_List structure used to track all open sockets               */
  uint8_t* RxBuffer,                /* INPUT -  A pointer to the incoming buffer containing a received message                         */
  int* RxBufferSize,                /* INPUT -  The size of the incoming buffer                                                        */
  uint8_t* TxBuffer,                /* OUTPUT - A pointer to the buffer containing the message response                                */
  int* TxBufferSize                 /* OUTPUT - The size of the outgoing buffer                                                        */
)
{
  /* declare local variables */
  unsigned int i, index, numberOfAdapters, adapter, numberOfOperations = 0, numberOfReadOperations = 0;
  unsigned int PHY_ID, MMD, address, data;
  unsigned int localBlockReadResult[MAX_BLOCK_OPERATION_SIZE];      /* used to store results in when local block operations are not supported locally */
  unsigned int *blockReadResult;
  char** adapterList;

#ifdef LOCKING_AND_TRACKING_SUPPORT
  SOCKET socketID;
  unsigned int j, socketID_Index;
  
  /* get the current socket ID */
  socketID = tcpSocketList->socketID[currentSocketID_Index];
#endif
  
  /* process the message */
  switch (RxBuffer[0])
  {
    case CODE_HW_Initialize:
      /*! HW_Initialize
          \verbatim

          Description: This command initializes the OS interface for the remote adapters, and returns information about
                       how many adapters there are and if it supports block operations.  It is required on some systems.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_Initialize
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          1               Max Block Ops MSW     maxBlockOperations[15:8]: 0x0000 means no block operation support
          2               Max Block Ops LSW     maxBlockOperations[7:0]
          3               Byte indicating how many adapters are at this IP address
          4->NULL...      Series of null-terminated strings containing the adapter names in adapter order
          \endverbatim
      */
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_Initialize () called from %s at %s\n",
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
                 
    #ifdef LOCKING_AND_TRACKING_SUPPORT
      switch (AddSocket (socketID, &HW_InterfaceInitializedState))
      {
        case 0:
          /* Fail - reached the maximum number of open sockets */
          TxBuffer[0] = 1;
          sprintf ((char*) &TxBuffer[1], "Socket Queue for HW_Initialize() is full: unable to add new TCP socket");
          *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
          return 0;
          
        case 1:
    #endif
          /* this is the first socket to initialize the interface */
          HW_Initialize ();
          if (HW_Fail())
          {
            TxBuffer[0] = 1;
            strcpy ((char*) &TxBuffer[1], HW_Error());
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
              
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
              
            return 0;
          }
    
    #ifdef LOCKING_AND_TRACKING_SUPPORT
          /* initialize the adapter tracking machinery */
          numberOfAdapters = HW_NumberOfDevices ();
          AdapterArray = (Adapter_State*) malloc (numberOfAdapters * sizeof (Adapter_State));
          for (i = 0; i < numberOfAdapters; i++)
          {
            AdapterArray[i].locked = 0;
            AdapterArray[i].lockingSocketID = 0;
            InitializeOpenedState (&(AdapterArray[i].state));
            for (j = 0; j < MAX_NUMBER_OF_PHYS; j++)
            {
              AdapterArray[i].phyArray[j].locked = 0;
              AdapterArray[i].phyArray[j].lockingSocketID = 0;
            }
          }
          break;
          
        case 2:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Interface is already initialized, so proceeding without calling HW_Initialize()\n");
          break;
          
        case 3:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Socket Queue for HW_Initialize() already contains entry for %s at %s, so proceeding without calling HW_Initialize()\n",
                     tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
          break;
            
        default:
          break;
      }
    #endif

      /* create the response message */
      TxBuffer[0] = 0;
      
      if (AQ_API_MDIO_SupportsBlockOperations ())
      {
        numberOfOperations = AQ_API_MDIO_MaxBlockOperations ();
        LocalBlockOperationSupport = 1;
      }
      else
      {
        numberOfOperations = MAX_BLOCK_OPERATION_SIZE;
        LocalBlockOperationSupport = 0;
      }
      TxBuffer[1] = (uint8_t) ((numberOfOperations >> 8) & 0xFF);
      TxBuffer[2] = (uint8_t) (numberOfOperations & 0xFF);
    
      numberOfAdapters = HW_NumberOfDevices ();
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "%i adapters\n", numberOfAdapters);
    
      TxBuffer[3] = (uint8_t) numberOfAdapters;

      /* copy the adapter list */
      index = 4;
      adapterList = HW_DeviceList ();
      for (i = 0; i < numberOfAdapters; i++)
      {
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Adapter %i = %s\n", i, adapterList[i]);
        strcpy ((char*) &(TxBuffer[index]), adapterList[i]);
        index += strlen (adapterList[i]) + 1;    /* +1 for null terminator */
      }
      *TxBufferSize = index;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");        
      break;

    case CODE_HW_OpenAdapter:
      /*! HW_OpenAdapter
          \verbatim

          Description: This opens a remote adapter, and is required on some systems before talking to the PHYs.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_OpenAdapter
          1               Adapter Number        adapterNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          1               Default Adapter       Default Adapter Number
          \endverbatim
      */
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_OpenAdapter (%i) called from %s at %s\n", RxBuffer[1],
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
        
      /* open the adapter - Note that locking only affects the ability to read and write board-level functions, so this doesn't matter here */
      adapter = RxBuffer[1];
      
    #ifdef LOCKING_AND_TRACKING_SUPPORT
      switch (AddSocket (socketID, &(AdapterArray[adapter].state)))
      {
        case 0:
          /* Fail - reached the maximum number of open sockets */
          TxBuffer[0] = 1;
          sprintf ((char*) &TxBuffer[1], "Socket Queue for HW_OpenAdapter() is full: unable to add new TCP socket");
          *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
          return 0;
          
        case 1:
    #endif
          /* this is the first socket to initialize the adapter */
          HW_OpenAdapter (adapter);
          if (HW_Fail())
          {
            TxBuffer[0] = 1;
            strcpy ((char*) &TxBuffer[1], HW_Error());
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
              
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
              
            return 0;
          }
          break;
          
    #ifdef LOCKING_AND_TRACKING_SUPPORT
        case 2:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Adapter is already initialized, so proceeding without calling HW_OpenAdapter()\n");
          break;
          
        case 3:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Socket Queue for HW_OpenAdapter() already contains socket ID #%i, so proceeding without calling HW_OpenAdapter()\n", socketID);
          break;
            
        default:
          break;
      }
    #endif
       
      /* get the default adapter */   
      data = HW_GetDefaultAdapter ();
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Default adapter = %i\n", data);
        
      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      
      /* return clean open */  
      TxBuffer[0] = 0;
      TxBuffer[1] = (uint8_t) data;
      *TxBufferSize = 2;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;

    case CODE_HW_CloseAdapter:
      /*! HW_CloseAdapter
          \verbatim

          Description: This closes a remote adapter.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_CloseAdapter
          1               Adapter Number        adapterNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_CloseAdapter (%i) called from %s at %s\n", RxBuffer[1],
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

      adapter = RxBuffer[1];
      
    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* Go through the list of PHYs and remove any locks for this socket ID in the PHY locks */
      for (j = 0; j < MAX_NUMBER_OF_PHYS; j++)
      {
        if (AdapterArray[adapter].phyArray[j].lockingSocketID == socketID)
        {
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Deleting lock for this socket (socket ID = %i) on PHY #%i\n", socketID, j);
            
          AdapterArray[adapter].phyArray[j].locked = 0;
          AdapterArray[adapter].phyArray[j].lockingSocketID = 0;
        }
      }
        
      switch (DeleteSocket (socketID, &(AdapterArray[adapter].state)))
      {
        case 0:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Unable to find socket ID #%i in the list for adapter #%i, but proceeding anyway\n", socketID, adapter);
          break;
       
        case 1:
    #endif
          /* this was the last socket on this adapter, so close the adapter*/
          HW_CloseAdapter (RxBuffer[1]);
          if (HW_Fail())
          {
            TxBuffer[0] = 1;
            strcpy ((char*) &TxBuffer[1], HW_Error());
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
              
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
            return 0;
          }
          break;
          
    #ifdef LOCKING_AND_TRACKING_SUPPORT
        case 2:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Other sockets are still using this adapter, so proceeding without calling HW_CloseAdapter()\n");
          break;
          
        default:
          break;          
      }
    #endif
      
      /* return a clean close */  
      TxBuffer[0] = 0;
      *TxBufferSize = 1;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;

    case CODE_HW_Close:
    case CODE_HW_Close_Internal:
      /*! HW_Close
          \verbatim

          Description: This closes the OS interface to the remote adapters and is required on some systems.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_Close
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_Close() called from %s at %s\n",
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* Go through the list and remove all socket entries for this socket ID in the adapters and PHY locks */
      numberOfAdapters = HW_NumberOfDevices ();
      for (i = 0; i < numberOfAdapters; i++)
      {
        for (j = 0; j < MAX_NUMBER_OF_PHYS; j++)
        {
          if (AdapterArray[i].phyArray[j].lockingSocketID == socketID)
          {
            AdapterArray[i].phyArray[j].locked = 0;
            AdapterArray[i].phyArray[j].lockingSocketID = 0;
          }
        }
        if (AdapterArray[i].lockingSocketID == socketID)
        {
          AdapterArray[i].locked = 0;
          AdapterArray[i].lockingSocketID = 0;
        }
      }
      
      switch (DeleteSocket (socketID, &HW_InterfaceInitializedState))
      {
        case 0:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Unable to find socket ID #%i in the list for this interface, so not calling HW_Close (), but proceeding anyway\n", socketID);
          break;
       
        case 1:
          /* this was the last socket on this machine, so close the OS I/F */
          free (AdapterArray);
    #endif
          
    #ifndef LOCKING_AND_TRACKING_SUPPORT
        if (RxBuffer[0] != CODE_HW_Close_Internal)
        {
    #endif
          HW_Close ();
          if (HW_Fail())
          {
            TxBuffer[0] = 1;
            strcpy ((char*) &TxBuffer[1], HW_Error());
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
              
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
            return 0;
          }
    #ifndef LOCKING_AND_TRACKING_SUPPORT
        }
    #endif
          break;
          
    #ifdef LOCKING_AND_TRACKING_SUPPORT
        case 2:
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Other sockets are still using this interface, so proceeding without calling HW_Close()\n");
          break;
          
        default:
          break;          
      }
    #endif
      
      TxBuffer[0] = 0;
      *TxBufferSize = 1;
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;

    case CODE_HW_Write:
      /*! HW_Write
          \verbatim

          Description: This writes data to the remote PHY.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_Write
          1               Adapter Number        adapterNumber
          2               PHY ID                phyNumber
          3               MMD                   MMD
          4               Address MSB           address[15:8]
          5               Address LSB           address[7:0]
          6               Data MSB              data[15:8]
          7               Data LSB              data[7:0]
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      adapter = RxBuffer[1];
      PHY_ID = RxBuffer[2];
      MMD = RxBuffer[3];
      address = ((unsigned int) (RxBuffer[4] << 8)) | ((unsigned int) RxBuffer[5]);
      data = ((unsigned int) (RxBuffer[6] << 8)) | ((unsigned int) RxBuffer[7]);
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_Write (%i, %i, 0x%.2X, 0x%.4X, 0x%.4X) called from %s at %s\n", adapter, PHY_ID, MMD, address, data,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* verify that this PHY is not locked by another process */
      if (IsPhyLocked(tcpSocketList, TxBuffer, TxBufferSize, adapter, PHY_ID, socketID, "write"))
        return 0;
    #endif
      
      HW_Write (adapter, PHY_ID, MMD, address, data);
      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        TxBuffer[0] = 0;
        *TxBufferSize = 1;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;

    case CODE_HW_Read:
      /*! HW_Read
          \verbatim

          Description: This reads data from the remote PHY.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_Read
          1               Adapter Number        adapterNumber
          2               PHY ID                phyNumber
          3               MMD                   MMD
          4               Address MSB           address[15:8]
          5               Address LSB           address[7:0]
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          1               Data MSB              data[15:8]
          2               Data LSB              data[7:0]
          \endverbatim
      */
      adapter = RxBuffer[1];
      PHY_ID = RxBuffer[2];
      MMD = RxBuffer[3];
      address = ((unsigned int) (RxBuffer[4] << 8)) | ((unsigned int) RxBuffer[5]);
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_Read (%i, %i, 0x%.2X, 0x%.4X) called from %s at %s\n", adapter, PHY_ID, MMD, address,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* verify that this PHY is not locked by another process */
      if (IsPhyLocked(tcpSocketList, TxBuffer, TxBufferSize, adapter, PHY_ID, socketID, "read"))
        return 0;
    #endif
      
      data = HW_Read (adapter, PHY_ID, MMD, address);
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Read result: 0x%.4X\n", data);

      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        TxBuffer[0] = 0;
        TxBuffer[1] = (uint8_t) (data >> 8);
        TxBuffer[2] = (uint8_t) (data & 0xFF);
        *TxBufferSize = 3;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;


    case CODE_HW_BlockOperationExecuteAdapter:
      /*! HW_BlockOperationExecuteAdapter
          \verbatim

          Description: This sends a block of read and write operations to the remote PHY.  The block read and write
                       operations are stored at the client prior to execution.  If the local device supports block operations,
                       they are called as block operations, otherwise they are handled as individual reads and writes
          
          Tx message format:
          
            Byte            Description           Value
            --------------------------------------------
            0               Message Type          CODE_HW_BlockOperationExecuteAdapter
            1               Adapter Number        adapterNumber
            2               PHY ID                phyNumber
            3               # Of Operations MSB   numberOfOperations[15:8]
            4               # Of Operations MSB   numberOfOperations[7:0]
            5               Read/MMD              Read* / Write in Bit 7, MMD in 4:0
            6               Address MSB           address[15:8]
            7               Address LSB           address[7:0]
            [8]             Data MSB              data[15:8]
            [9]             Data LSB              data[7:0]
            .
            .
            .
          
            Rx message format:
          
            Byte            Description           Value
            --------------------------------------------
            0               Error Flag            Set if there is an error
            [1->NULL]       Message               If no error, no message is provided and this has no length
            1               Number Of Reads MSB   numberOfReads[15:8]
            2               Number Of Reads LSB   numberOfReads[7:0]
            [3]             Data MSB              data[15:8]
            [4]             Data LSB              data[7:0]
            .
            .
            .
           \endverbatim
      */
      adapter = RxBuffer[1];
      PHY_ID = RxBuffer[2];
      numberOfOperations = ((unsigned int) (RxBuffer[3] << 8)) | ((unsigned int) RxBuffer[4]);

      if (TCP_VERBOSE)
      {
        fprintf (TCP_VerboseServerOutput, "HW_BlockOperationExecuteAdapter (%i, %i) called from %s at %s\n", adapter, PHY_ID,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
        fprintf (TCP_VerboseServerOutput, "  Number of Operations = %i:\n", numberOfOperations);
      }
      
    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* verify that this PHY is not locked by another process */
      if (IsPhyLocked(tcpSocketList, TxBuffer, TxBufferSize, adapter, PHY_ID, socketID, "requested block operations"))
        return 0;
    #endif
      
      /* store the block requests in the local queue and then execute the block operations */
      index = 5;
      
      if (!LocalBlockOperationSupport)
        numberOfReadOperations = 0;
    
      for (i = 0; i < numberOfOperations; i++)
      {
         MMD = RxBuffer[index] & 0x1F;
         address = ((unsigned int) (RxBuffer[index+1] << 8)) | ((unsigned int) RxBuffer[index+2]);
         if (RxBuffer[index] & 0x80)
         {
           /* write */
           data = ((unsigned int) (RxBuffer[index+3] << 8)) | ((unsigned int) RxBuffer[index+4]);
           
           if (TCP_VERBOSE)
             fprintf (TCP_VerboseServerOutput, "    Write: %.2X.%.4X <- 0x%.4X\n", MMD, address, data);

           if (LocalBlockOperationSupport)
             HW_BlockWriteAdapter (adapter, PHY_ID, MMD, address, data);
           else
             HW_Write (adapter, PHY_ID, MMD, address, data);         
           index += 5;
         }
         else
         {
           /* read */
           if (TCP_VERBOSE)
             fprintf (TCP_VerboseServerOutput, "    Read: %.2X.%.4X\n", MMD, address);

           if (LocalBlockOperationSupport)
             HW_BlockReadAdapter (adapter, PHY_ID, MMD, address);
           else
             localBlockReadResult[numberOfReadOperations++] = HW_Read (adapter, PHY_ID, MMD, address);         
           index += 3;
         }
      }

      if (LocalBlockOperationSupport)
        blockReadResult = HW_BlockOperationExecuteAdapter (adapter, PHY_ID);
      else
        blockReadResult = localBlockReadResult;

      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        /* we are good - load up the read results and send them back */
        TxBuffer[0] = 0;
        
        if (LocalBlockOperationSupport)
          numberOfReadOperations = HW_BlockReadQueueSize ();
          
        TxBuffer[1] = (uint8_t) (numberOfReadOperations >> 8);
        TxBuffer[2] = (uint8_t) (numberOfReadOperations & 0xFF);

        index = 3;
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "  Returning %i reads %i:\n", numberOfReadOperations, LocalBlockOperationSupport);

        for (i = 0; i < numberOfReadOperations; i++)
        {
          data = blockReadResult[i];
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "    0x%.4X\n", data);

          TxBuffer[index] = (uint8_t) (data >> 8);
          TxBuffer[index+1] = (uint8_t) (data & 0xFF);
          index += 2;
        }
        *TxBufferSize = index;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
  
  #ifdef BOARD_SUPPORT
    case CODE_Board_WriteAdapter:
      /*! Board_WriteAdapter
          \verbatim

          Description: This writes data to the remote adapter or board control - i.e. for voltage margining, etc.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_Board_WriteAdapter
          1               Adapter #             adapterNumber
          2               Address MSB           address[15:8]
          3               Address LSB           address[7:0]
          4               Data MSB              data[15:8]
          5               Data LSB              data[7:0]
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      adapter = RxBuffer[1];
      address = ((unsigned int) (RxBuffer[2] << 8)) | ((unsigned int) RxBuffer[3]);
      data = ((unsigned int) (RxBuffer[4] << 8)) | ((unsigned int) RxBuffer[5]);
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Board_WriteAdapter (%i, 0x%.4X, 0x%.4X) called from %s at %s\n", adapter, address, data,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* verify that this adapter is not locked by another process */
      if (IsAdapterLocked(tcpSocketList, TxBuffer, TxBufferSize, adapter, socketID, "board write"))
        return 0;
    #endif
      
      Board_WriteAdapter (adapter, address, data);
      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        TxBuffer[0] = 0;
        *TxBufferSize = 1;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;

    case CODE_Board_ReadAdapter:
      /*! Board_ReadAdapter
          \verbatim

          Description: This reads data from the remote adapter or board.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_Board_ReadAdapter
          1               Adapter #             adapterNumber
          2               Address MSB           address[15:8]
          3               Address LSB           address[7:0]
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          1               Data MSB              data[15:8]
          2               Data LSB              data[7:0]
          \endverbatim
      */
      adapter = RxBuffer[1];
      address = ((unsigned int) (RxBuffer[2] << 8)) | ((unsigned int) RxBuffer[3]);
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Board_ReadAdapter (%i, 0x%.4X) called from %s at %s\n", adapter, address,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
                 
    #ifdef LOCKING_AND_TRACKING_SUPPORT
      /* verify that this adapter is not locked by another process */
      if (IsAdapterLocked(tcpSocketList, TxBuffer, TxBufferSize, adapter, socketID, "board read"))
        return 0;
    #endif
      
      data = Board_ReadAdapter (adapter, address);

      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        TxBuffer[0] = 0;
        TxBuffer[1] = (uint8_t) (data >> 8);
        TxBuffer[2] = (uint8_t) (data & 0xFF);
        *TxBufferSize = 3;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
  #endif
  
    case CODE_HW_GetDefaultAdapter:
      /*! HW_GetDefaultAdapter
          \verbatim

          Description: This returns the number of the default adapter at the server.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_GetDefaultAdapter
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          1               Adapter #             adapterNumber
          \endverbatim
      */
      adapter = RxBuffer[1];
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_GetDefaultAdapter () called from %s at %s\n",
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

      data = HW_GetDefaultAdapter ();
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Default adapter = %i\n", data);
        
      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        TxBuffer[0] = 0;
        TxBuffer[1] = (uint8_t) data;
        *TxBufferSize = 2;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;

    case CODE_HW_SetDefaultAdapter:
      /*! HW_SetDefaultAdapter
          \verbatim

          Description: This sets the number of the default adapter at the server.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_SetDefaultAdapter
          1               Adapter #             adapterNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      adapter = RxBuffer[1];
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "HW_SetDefaultAdapter (%i) called from %s at %s\n", adapter,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);

      HW_SetDefaultAdapter (adapter);
      if (HW_Fail())
      {
        TxBuffer[0] = 1;
        strcpy ((char*) &TxBuffer[1], HW_Error());
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
          
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", HW_Error());
        return 0;
      }
      else
      {
        TxBuffer[0] = 0;
        *TxBufferSize = 1;
      }
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
      
  
    case CODE_Identify:
      /*! Identify
          \verbatim

          Description: This transmits the client's hostname to the server for use during error messaging - i.e. who has locked what
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_Identify
          [1->NULL]       Client Name           The name of the client machine.  This is sent during connecting to the server
                                                for use during locking adapters and PHYs
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          1               Board Support         1, if server supports board operations, 0 otherwise
          2               Tracking Support      1, if server supports tracking and locking, 0 otherwise
          \endverbatim
      */
      strcpy (tcpSocketList->hostName[currentSocketID_Index], (char*) &RxBuffer[1]);
      if (TCP_VERBOSE)
      {
        fprintf (TCP_VerboseServerOutput, "Identify () called from %s at %s on socket ID #%i, index #%i\n\n",
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index], tcpSocketList->socketID[currentSocketID_Index], currentSocketID_Index);
          
        /* dump the tcpSocketList */
        fprintf (TCP_VerboseServerOutput, "Current TCP Socket List:\n");
        fprintf (TCP_VerboseServerOutput, "Entry\t Name\t IP Address:\n");
        for (i = 0; i < tcpSocketList->numberOfClients; i++)
        {
          fprintf (TCP_VerboseServerOutput, "%i\t%s\t%s\n", tcpSocketList->socketID[i], tcpSocketList->hostName[i], tcpSocketList->ipAddress[i]);
        }
      }
      
      TxBuffer[0] = 0;
    #ifdef BOARD_SUPPORT
      TxBuffer[1] = 1;
    #else
      TxBuffer[1] = 0;
    #endif
    #ifdef LOCKING_AND_TRACKING_SUPPORT
      TxBuffer[2] = 1;
    #else
      TxBuffer[2] = 0;
    #endif
      *TxBufferSize = 3;
        
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
      
  #ifdef LOCKING_AND_TRACKING_SUPPORT
    case CODE_HW_LockAdapter:
      /*! Lock_Adapter
          \verbatim

          Description: This locks an adapter so that board reads and writes to an adapter can only be executed by the locking socket ID.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_LockAdapter
          1               Adapter #             adapterNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      
      adapter = RxBuffer[1];   
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Lock_Adapter (%i) called from %s at %s\n", adapter,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
                 
      /* check to make sure the interface is initialized, the adapter is open, and the PHY is unlocked */
      if (!HW_InterfaceInitializedState.numberOfActiveSockets)
      {
        TxBuffer[0] = 1;
        sprintf ((char*) &TxBuffer[1], "Attempting to lock adapter on unopened interface");
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
        
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", &TxBuffer[1]);
        return 0;
      }
      
      /* check to make sure the adapter is opened */ 
      if (!AdapterArray[adapter].state.numberOfActiveSockets)
      {
        TxBuffer[0] = 1;
        sprintf ((char*) &(TxBuffer[1]), "Attempting to lock unopened adapter #%i", adapter);
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
        
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", &TxBuffer[1]);
        return 0;
      }
      
      /* check to make sure the adapter is not locked */ 
      if (AdapterArray[adapter].locked)
      {
        /* check to make sure it is not already locked by the same socket ID */
        if (AdapterArray[adapter].lockingSocketID == socketID)
        {
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "%s at %s is attempting to relock adapter %i\n",
                     tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index], adapter);
        }
        else
        {        
          if ((socketID_Index = FindSocketID_Index (AdapterArray[adapter].lockingSocketID, tcpSocketList) >= 0))
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "Adapter is already locked by %s at %s",
                    tcpSocketList->hostName[socketID_Index], tcpSocketList->ipAddress[socketID_Index]);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
          else
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "Adapter is already locked by unknown socket ID %i.  This should not occur.",
                     AdapterArray[adapter].lockingSocketID);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
        }
      }  /* if (AdapterArray[adapter].locked) */
      
      /* we are good - lock the adapter */
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Adapter %i locked\n", adapter);
            
      AdapterArray[adapter].locked = 1;
      AdapterArray[adapter].lockingSocketID = socketID;
          
      TxBuffer[0] = 0;
      *TxBufferSize = 1;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
      
      
    case CODE_HW_UnlockAdapter:
      /*! Unlock_Adapter
          \verbatim

          Description: This locks a PHY so that reads and writes to a PHY can only be executed by the locking socket ID.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_UnlockAdapter
          1               Adapter #             adapterNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      
      adapter = RxBuffer[1];   
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Unlock_Adapter (%i) called from %s at %s\n", adapter,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
                 
      /* check to make sure the adapter is locked */ 
      if (AdapterArray[adapter].locked)
      {
        /* check to make sure it is locked by this socket ID */
        if (AdapterArray[adapter].lockingSocketID == socketID)
        {
          AdapterArray[adapter].locked = 0;
          AdapterArray[adapter].lockingSocketID = 0;
          
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "Adapter %i unlocked\n", adapter);            
        }
        else
        {        
          if ((socketID_Index = FindSocketID_Index (AdapterArray[adapter].lockingSocketID, tcpSocketList) >= 0))
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "Unable to unlock adapter as it is locked by %s at %s",
                    tcpSocketList->hostName[socketID_Index], tcpSocketList->ipAddress[socketID_Index]);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
          else
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "Unable to unlock adapter as it is locked by unknown socket ID %i.  This should not occur.",
                     AdapterArray[adapter].lockingSocketID);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
        }
      }
      else
      {
        /* Adapter was not locked */
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Adapter %i was not locked\n", adapter);
      }  /* if (AdapterArray[adapter].locked) */
      
      TxBuffer[0] = 0;
      *TxBufferSize = 1;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
      
      
    case CODE_HW_LockPHY:
      /*! Lock_PHY
          \verbatim

          Description: This locks a PHY so that reads and writes to a PHY can only be executed by the locking socket ID.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_LockPHY
          1               Adapter #             adapterNumber
          2               PHY ID                phyNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      
      adapter = RxBuffer[1];   
      PHY_ID = RxBuffer[2];
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Lock_PHY (%i, %i) called from %s at %s\n", adapter, PHY_ID,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
                 
      /* check to make sure the interface is initialized, the adapter is open, and the PHY is unlocked */
      if (!HW_InterfaceInitializedState.numberOfActiveSockets)
      {
        TxBuffer[0] = 1;
        sprintf ((char*) &TxBuffer[1], "Attempting to lock PHY on unopened interface");
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
        
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", &TxBuffer[1]);
        return 0;
      }
      
      /* check to make sure the adapter is opened */ 
      if (!AdapterArray[adapter].state.numberOfActiveSockets)
      {
        TxBuffer[0] = 1;
        sprintf ((char*) &(TxBuffer[1]), "Attempting to lock PHY on unopened adapter #%i", adapter);
        *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
        
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "Error: %s\n", &TxBuffer[1]);
        return 0;
      }
      
      /* check to make sure the PHY is not locked */ 
      if (AdapterArray[adapter].phyArray[PHY_ID].locked)
      {
        /* check to make sure it is not already locked by the same socket ID */
        if (AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID == socketID)
        {
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "%s at %s is attempting to relock PHY %i on adapter %i\n",
                     tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index], adapter, PHY_ID);
        }
        else
        {        
          if ((socketID_Index = FindSocketID_Index (AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID, tcpSocketList) >= 0))
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "PHY is already locked by %s at %s",
                    tcpSocketList->hostName[socketID_Index], tcpSocketList->ipAddress[socketID_Index]);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
          else
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "PHY is already locked by unknown socket ID %i.  This should not occur.",
                     AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
        }
      }  /* if (AdapterArray[adapter].phyArray[PHY_ID].locked) */
      
      /* we are good - lock the PHY */
      AdapterArray[adapter].phyArray[PHY_ID].locked = 1;
      AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID = socketID;
          
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "PHY %i on adapter %i locked\n", PHY_ID, adapter);
            
      TxBuffer[0] = 0;
      *TxBufferSize = 1;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
      
      
    case CODE_HW_UnlockPHY:
      /*! Unlock_PHY
          \verbatim

          Description: This locks a PHY so that reads and writes to a PHY can only be executed by the locking socket ID.
          
          Tx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Message Type          CODE_HW_UnlockPHY
          1               Adapter #             adapterNumber
          2               PHY ID                phyNumber
          
          Rx message format:
          
          Byte            Description           Value
          --------------------------------------------
          0               Error Flag            Set if there is an error
          [1->NULL]       Message               If no error, no message is provided and this has no length
          \endverbatim
      */
      
      adapter = RxBuffer[1];   
      PHY_ID = RxBuffer[2];
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Unlock_PHY (%i, %i) called from %s at %s\n", adapter, PHY_ID,
                 tcpSocketList->hostName[currentSocketID_Index], tcpSocketList->ipAddress[currentSocketID_Index]);
                 
      /* check to make sure the PHY is locked by this socket ID */ 
      if (AdapterArray[adapter].phyArray[PHY_ID].locked)
      {
        /* check to make sure it is not already locked by the same socket ID */
        if (AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID == socketID)
        {
          if (TCP_VERBOSE)
            fprintf (TCP_VerboseServerOutput, "PHY %i on adapter %i unlocked\n", PHY_ID, adapter);
            
          AdapterArray[adapter].phyArray[PHY_ID].locked = 0;
          AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID = 0;
        }
        else
        {        
          if ((socketID_Index = FindSocketID_Index (AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID, tcpSocketList) >= 0))
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "Unable to unlock PHY as it is locked by %s at %s",
                    tcpSocketList->hostName[socketID_Index], tcpSocketList->ipAddress[socketID_Index]);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
          else
          {
            TxBuffer[0] = 1;
            sprintf ((char*) &TxBuffer[1], "Unable to unlock PHY as it is locked by unknown socket ID %i.  This should not occur.",
                     AdapterArray[adapter].phyArray[PHY_ID].lockingSocketID);
            *TxBufferSize = (int) strlen ((char*) &TxBuffer[1]) + 2;         /* Fail byte + null terminator on string */
            
            if (TCP_VERBOSE)
              fprintf (TCP_VerboseServerOutput, "%s\n", &TxBuffer[1]);
              
            return 0;
          }
        }
      }
      else
      {
        /* PHY was not locked */
        if (TCP_VERBOSE)
          fprintf (TCP_VerboseServerOutput, "PHY %i on adapter %i was not locked\n", PHY_ID, adapter);
      }  /* if (AdapterArray[adapter].phyArray[PHY_ID].locked) */
      
      TxBuffer[0] = 0;
      *TxBufferSize = 1;
      
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "\n");
      break;
  #endif
      
    default:
      if (TCP_VERBOSE)
        fprintf (TCP_VerboseServerOutput, "Unknown instruction %i\n\n", RxBuffer[0]);
      break;
  }

  return 1;
}

/*@}*/

#ifndef BLOCK_OPERATION_SUPPORT
unsigned int AQ_API_MDIO_MaxBlockOperations ()
{
  return 0;
}

unsigned int HW_BlockReadAdapter (unsigned int adapter, unsigned int PHY_ID, unsigned int MMD, unsigned int address)
{
  return 0;
}

void HW_BlockWriteAdapter (unsigned int adapter, unsigned int PHY_ID, unsigned int MMD, unsigned int address, unsigned int data)
{
}

unsigned int* HW_BlockOperationExecuteAdapter (unsigned int adapter, unsigned int PHY_ID)
{
  return NULL;
}

unsigned int HW_BlockReadQueueSize ()
{
  return 0;
}
#endif /* BLOCK_OPERATION_SUPPORT */

#ifdef __cplusplus
}
#endif

